package com.boot.mall.ware.mq;

import com.boot.common.constant.MqDestinationConstant;
import com.boot.common.enume.OrderStatusEnum;
import com.boot.common.to.mq.StockLockedTo;
import com.boot.common.to.order.OrderStatusTo;
import com.boot.mall.ware.feign.OrderFeignService;
import com.boot.mall.ware.service.WareSkuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Service;

import javax.jms.JMSException;
import javax.jms.Message;

@Service
public class StockReleaseListener {

    @Autowired
    private OrderFeignService orderFeignService;
    @Autowired
    private WareSkuService wareSkuService;
    @Autowired
    private JmsProducerService jmsProducerService;

    /**
     * 监听消息队列，收到延迟消息时解锁库存
     * @param to
     * @param message
     * @throws JMSException
     */
    @JmsListener(destination = MqDestinationConstant.ORDER_QUEUE)
    public void handlerReleaseStockLocked(StockLockedTo to, Message message) throws JMSException {
        /**
         *  库存解锁情况：
         *      订单创建的时候，订单创建时只要出现异常，创建的订单信息和订单项会回滚
         *      反过来说，只要数据库中有订单表的数据信息，即表示订单是创建成功的
         *      只要MQ不宕机，就会给MQ服务器发送订单任务的数据信息，其中订单任务是在发送消息之前创建的
         *      一句话：只要订单是创建成功的，订单任务创建成功，才会给MQ生产延迟消息，这里设置的时间是1分钟，可以设置15分钟
         *      消费者对指定的队列进行监听，当收到队列的消息时，包含以下的信息：
         *      id=8,orderSn=.., detailToList=[StockDetailTo(id=8, skuId=6, skuName=null, skuNum=1, taskId=8, wareId=1, lockStatus=1)]
         *      因为这里存在分布式事务问题，我没有解决，但是这里暂时也没有错误。
         *      当库存锁定远程调用成功之后，但是在这个调用的方法里面调用的方法代码之后出现异常，会导致已经锁定的库存无法回滚，因此分布式事务是十分有必要的
         *      当出现这个问题的时候，库存会被锁定，但是订单创建失败，也就是说数据库中没有这条订单的数据存在
         *      因此，当消费者拿到消息之后，
         *          1、首先根据订单号查询订单表中是否存在该订单，当订单不存在时，将锁定的库存解锁
         *          2、当该订单存在时，获取订单的状态信息
         *              1)、已取消状态则解锁库存
         *              2)、当订单状态还是待付款状态时则解锁库存
         *                  因为发送的时候延迟消息，也就是说消息会在指定时间之后发送到队列，所以当收到消息之后
         *                  查询数据库订单状态还是待付款状态，则将库存解锁
         *          3、当付款完成之后，将锁定状态的数量减去，并且库存减少
         *      TODO https://www.bilibili.com/video/BV1np4y1C7Yf?p=296&spm_id_from=pageDriver
         */
        System.out.println("接收到即将取消的订单信息....");
        // 1.根据orderSn查询订单的信息
        String orderSn = to.getOrderSn();      //订单号
        OrderStatusTo orderStatus = orderFeignService.getOrderStatus(orderSn);

        // 2.判断订单是否存在，订单不存在则按照锁定的任务单解锁库存，判断订单的状态是否为“已取消”或者“待付款”状态，是则将库存解锁
        // 其中  “待付款”状态为自动解锁库存   “不存在”状态为创建订单出错而自动解锁库存
        Integer status = orderStatus.getStatus();
        if (orderStatus == null || status == OrderStatusEnum.CANCLED.getCode() || status == OrderStatusEnum.CREATE_NEW.getCode()){
            //订单还未支付或者还是待付款状态，解锁库存，订单不存在，说明保存订单时出错，解锁库存
            try {
                wareSkuService.releaseStock(orderSn,status);
                //解锁成功，对消息进行签收
                message.acknowledge();
            } catch (Exception e) {
                System.out.println("库存解锁失败");
                // 库存解锁失败，将此条消息重新发送给MQ等待其他的消费者处理
                jmsProducerService.sendMessage(MqDestinationConstant.ORDER_QUEUE,to);
            }
        }
        //  订单处于其他状态，什么也不做 当消息被消费者接收到时，而且以上判断也不通过，则什么都不要处理不需要
    }

}
